package main.disk;


import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;

/**
 * 模拟物理磁盘的类
 * 使用一个文件模拟磁盘，提供块的读写操作
 */
public class Disk implements AutoCloseable {
    // 磁盘参数常量
    public static final int DISK_SIZE = 128;         // 磁盘总块数
    public static final int BLOCK_SIZE = 64;         // 每个块的大小（字节）
    public static final int FAT_START_BLOCK = 0;     // FAT起始块号
    public static final int FAT_BLOCKS = 2;          // FAT占用的块数
    public static final int ROOT_DIR_BLOCK = 2;       // 根目录起始块号

    private RandomAccessFile diskFile;               // 模拟磁盘的文件
    private final String diskFileName;               // 磁盘文件名

    /**
     * 构造函数：创建或打开模拟磁盘文件
     * @param filename 磁盘文件名
     * @param createNew 是否创建新磁盘
     * @throws DiskException 磁盘操作异常
     */
    public Disk(String filename, boolean createNew) throws DiskException {
        this.diskFileName = filename;

        try {
            diskFile = new RandomAccessFile(filename, createNew ? "rw" : "rws");

            if (createNew) {
                format(); // 格式化新磁盘
            }
        } catch (IOException e) {
            throw new DiskException("无法创建或打开磁盘文件: " + filename, e);
        }
    }

    /**
     * 读取指定磁盘块的内容
     * @param blockNum 块号 (0-DISK_SIZE-1)
     * @param buffer 输出缓冲区
     * @throws DiskException 磁盘操作异常
     */
    public void readBlock(int blockNum, byte[] buffer) throws DiskException {
        validateBlockNumber(blockNum);
        if (buffer.length != BLOCK_SIZE) {
            throw new DiskException("缓冲区大小必须为 " + BLOCK_SIZE + " 字节");
        }

        try {
            diskFile.seek(blockNum * (long) BLOCK_SIZE);
            diskFile.readFully(buffer);
        } catch (IOException e) {
            throw new DiskException("读取块 " + blockNum + " 失败", e);
        }
    }

    /**
     * 将数据写入指定磁盘块
     * @param blockNum 块号 (0-DISK_SIZE-1)
     * @param buffer 要写入的数据
     * @throws DiskException 磁盘操作异常
     */
    public void writeBlock(int blockNum, byte[] buffer) throws DiskException {
        validateBlockNumber(blockNum);
        if (buffer.length != BLOCK_SIZE) {
            throw new DiskException("缓冲区大小必须为 " + BLOCK_SIZE + " 字节");
        }

        try {
            diskFile.seek(blockNum * (long) BLOCK_SIZE);
            diskFile.write(buffer);
        } catch (IOException e) {
            throw new DiskException("写入块 " + blockNum + " 失败", e);
        }
    }

    /**
     * 格式化磁盘：将所有块初始化为0
     * @throws DiskException 磁盘操作异常
     */
    public void format() throws DiskException {
        try {
            diskFile.setLength(0); // 清空文件
            byte[] emptyBlock = new byte[BLOCK_SIZE];
            Arrays.fill(emptyBlock, (byte) 0);

            // 写入所有块
            for (int i = 0; i < DISK_SIZE; i++) {
                writeBlock(i, emptyBlock);
            }
        } catch (IOException e) {
            throw new DiskException("格式化磁盘失败", e);
        }
    }

    /**
     * 验证块号是否有效
     * @param blockNum 要验证的块号
     * @throws DiskException 如果块号无效
     */
    private void validateBlockNumber(int blockNum) throws DiskException {
        if (blockNum < 0 || blockNum >= DISK_SIZE) {
            throw new DiskException("无效的块号: " + blockNum + ". 必须在 0 到 " + (DISK_SIZE - 1) + " 之间");
        }
    }

    /**
     * 关闭磁盘文件
     * @throws DiskException 磁盘操作异常
     */
    @Override
    public void close() throws DiskException {
        try {
            if (diskFile != null) {
                diskFile.close();
            }
        } catch (IOException e) {
            throw new DiskException("关闭磁盘文件失败", e);
        }
    }

    /**
     * 获取磁盘文件名
     * @return 磁盘文件名
     */
    public String getDiskFileName() {
        return diskFileName;
    }

    /**
     * 获取磁盘总大小（字节）
     * @return 磁盘总大小
     */
    public long getTotalSize() {
        return (long) DISK_SIZE * BLOCK_SIZE;
    }
}